/* Turska Framework/UI library (Turs2)
 * Copyright 2003-2009 Jetro Lauha
 * All rights reserved.
 * Web: http://iki.fi/jetro/turska/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of EITHER:
 *   (1) The GNU Lesser General Public License as published by the Free
 *       Software Foundation; either version 2.1 of the License, or (at
 *       your option) any later version. The text of the GNU Lesser
 *       General Public License is included with this library in the
 *       file LICENSE-LGPL.txt.
 *   (2) The BSD-style license that is included with this library in
 *       the file LICENSE-BSD.txt.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
 * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
 *
 * $Id: T2ImageIrr.cpp 131 2009-06-05 05:58:05Z jlauha $
 * $Revision: 131 $
 */

#include "Turs2.h"

#ifdef T2_USE_IRR

#include <assert.h>

using namespace irr;

namespace turska
{


ImageIrr::ImageIrr(GraphicsIrr *graphics,
                   UI32 width, UI32 height, UI32 flags,
                   irr::video::ITexture *texture, const irr::core::vector2df *uv) :
    Image(width, height, IDM_NORMAL, flags),
    mGraphics(graphics),
    mDriver(0),
    mTexture(texture),
    mHalfWidth((F32)width / 2.0f),
    mHalfHeight((F32)height / 2.0f),
    mTargetWidth((F32)width),
    mTargetHeight((F32)height),
    mAngle(0),
    mScaleX(1), mScaleY(1),
    mUsingExplicitTargetSize(false)
{
    assert(graphics);
    if (graphics)
        mDriver = graphics->getIrrDevice()->getVideoDriver();
    mColor.set(0xffffffff);
    mUV[0].set(uv[0]);
    mUV[1].set(uv[1]);
    if (texture)
        texture->grab();
}


ImageIrr::~ImageIrr()
{
    if (mTexture)
        mTexture->drop();
}


void ImageIrr::setColor(const Color &color)
{
    mColor.set(color.a, color.r, color.g, color.b);
}


void ImageIrr::draw(IMAGE_DRAW_MODE drawMode, UI32 flags, REAL x, REAL y,
                    const Color &color, const Rectangle *sourceRect)
{
    video::SColor irrcolor(color.a, color.r, color.g, color.b);
    draw(drawMode, flags | IF_FILTER_LINEAR, x, y, irrcolor, sourceRect);
}


void ImageIrr::draw(IMAGE_DRAW_MODE drawMode, UI32 flags, F32 x, F32 y,
                    const irr::video::SColor &color, const Rectangle *sourceRect)
{
    bool usingSrcArea = true;
    Rectangle srcArea;
    if (sourceRect)
        srcArea = *sourceRect;
    else if (!sourceRect && mUsingSourceRectangle)
        srcArea = mSourceRectangle;
    else
    {
        srcArea = Rectangle(0, 0, (REAL)mWidth, (REAL)mHeight);
        usingSrcArea = false;
    }

    video::SMaterial material = mGraphics->getDefaultMaterial();

    if (drawMode != IDM_NORMAL)
    {
        switch (drawMode)
        {
        case IDM_ADD:
            material.MaterialTypeParam = video::pack_texureBlendFunc(video::EBF_SRC_ALPHA, video::EBF_ONE, video::EMFN_MODULATE_1X);
            break;
        case IDM_MULTIPLY:
            material.MaterialTypeParam = video::pack_texureBlendFunc(video::EBF_ZERO, video::EBF_SRC_COLOR, video::EMFN_MODULATE_1X);
            break;
        case IDM_PREMULT:
            material.MaterialTypeParam = video::pack_texureBlendFunc(video::EBF_ONE, video::EBF_ONE_MINUS_SRC_ALPHA, video::EMFN_MODULATE_1X);
            break;
        }
    }
    if (mFlags & IF_WRAP_REPEAT)
    {
        // setFlag in API is just for bool in Irr 1.4, so we're setting the wrap manually
        //mDefaultMaterial.setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
        //for (u32 a = 0; a < video::MATERIAL_MAX_TEXTURES; ++a)
        //    mDefaultMaterial.TextureLayer[a].TextureWrap = video::ETC_REPEAT;
        // enough to set just first texture layer as we're using EMT_ONETEXTURE_BLEND
        material.TextureLayer[0].TextureWrap = video::ETC_REPEAT;
    }
    if (mFlags & IF_FILTER_LINEAR_MIPMAP)
        material.setFlag(video::EMF_TRILINEAR_FILTER, true);
    if (mFlags & IF_FILTER_LINEAR)
        material.setFlag(video::EMF_BILINEAR_FILTER, true);

    material.setTexture(0, mTexture);
    mDriver->setMaterial(material);

    video::S3DVertex vertices[4];
    if (flags & IF_COLOR)
    {
        // Set vertex color & alpha (IF_ALPHA flag is handled below separately)
        for (I32 a = 0; a < 4; ++a)
            vertices[a].Color = color;
    }
    else
    {
        for (I32 a = 0; a < 4; ++a)
            vertices[a].Color.set(0xffffffff);
    }
    if (!(flags & IF_ALPHA))
    {
        // ALPHA flag is not set, reset vertex alpha
        for (I32 a = 0; a < 4; ++a)
            vertices[a].Color.color |= 0xff000000;
    }
    else if (!(flags & IF_COLOR))
    {
        // ALPHA is set but not color flag, copy alpha values
        for (I32 a = 0; a < 4; ++a)
            vertices[a].Color.setAlpha(color.getAlpha());
    }

    irr::core::vector2df uv[2];
    uv[0] = mUV[0]; uv[1] = mUV[1];
    if (flags & IF_FLIP_HORIZ)
    {
        uv[0].X = mUV[1].X;
        uv[1].X = mUV[0].X;
    }
    if (flags & IF_FLIP_VERT)
    {
        uv[0].Y = mUV[1].Y;
        uv[1].Y = mUV[0].Y;
    }
    F32 halfWidth = mHalfWidth, halfHeight = mHalfHeight;
    if (usingSrcArea)
    {
        F32 uvW = uv[1].X - uv[0].X;
        F32 uvH = uv[1].Y - uv[0].Y;
        uv[0].X += uvW * srcArea.x / mWidth;
        uv[0].Y += uvH * srcArea.y / mHeight;
        uv[1].X = uv[0].X + uvW * srcArea.width / mWidth;
        uv[1].Y = uv[0].Y + uvH * srcArea.height / mHeight;
        halfWidth = srcArea.width * 0.5f;
        halfHeight = srcArea.height * 0.5f;
    }

    vertices[0].TCoords.set(uv[0].X, uv[0].Y);
    vertices[1].TCoords.set(uv[1].X, uv[0].Y);
    vertices[2].TCoords.set(uv[0].X, uv[1].Y);
    vertices[3].TCoords.set(uv[1].X, uv[1].Y);

    if (flags & IF_TRANSFORM)
    {
        F32 pivotX = -halfWidth, pivotY = -halfHeight; // transform center adjusted to top-left (default)
        if (flags & IF_ALIGN_RIGHT)
            pivotX = halfWidth;
        if (flags & IF_ALIGN_HORIZ_CENTER)
            pivotX = (mUsingCustomPivot ? mPivotX : halfWidth) - halfWidth;
        if (flags & IF_ALIGN_BOTTOM)
            pivotY = halfHeight;
        if (flags & IF_ALIGN_VERT_CENTER)
            pivotY = (mUsingCustomPivot ? mPivotY : halfHeight) - halfHeight;

        core::matrix4 transform;

        transform.setTranslation(core::vector3df(x, y, 0));
        if (flags & IF_ROTATE)
            transform.setRotationRadians(core::vector3df(0, 0, mAngle));
        if (flags & IF_SCALE)
        {
            core::matrix4 scale;
            scale.setScale(core::vector3df(mScaleX, mScaleY, 0));
            transform *= scale;
        }

        vertices[0].Pos.set(-halfWidth - pivotX, -halfHeight - pivotY, 0);
        vertices[1].Pos.set(halfWidth - pivotX, -halfHeight - pivotY, 0);
        vertices[2].Pos.set(-halfWidth - pivotX, halfHeight - pivotY, 0);
        vertices[3].Pos.set(halfWidth - pivotX, halfHeight - pivotY, 0);
        transform.transformVect(vertices[0].Pos);
        transform.transformVect(vertices[1].Pos);
        transform.transformVect(vertices[2].Pos);
        transform.transformVect(vertices[3].Pos);
    }
    else
    {
        F32 targetWidth = mTargetWidth, targetHeight = mTargetHeight;
        if (usingSrcArea && !mUsingExplicitTargetSize)
        {
            targetWidth = srcArea.width;
            targetHeight = srcArea.height;
        }
        F32 topLeftX = x, topLeftY = y; // top-left (default)
        if (flags & IF_ALIGN_RIGHT)
            topLeftX -= targetWidth;
        if (flags & IF_ALIGN_HORIZ_CENTER)
        {
            const F32 normalized = ((mUsingCustomPivot ? mPivotX : halfWidth) - halfWidth) / halfWidth; // range -1..1
            const F32 halfTargetWidth = targetWidth * 0.5f;
            topLeftX -= halfTargetWidth + halfTargetWidth * normalized;
        }
        if (flags & IF_ALIGN_BOTTOM)
            topLeftY -= targetHeight;
        if (flags & IF_ALIGN_VERT_CENTER)
        {
            const F32 normalized = ((mUsingCustomPivot ? mPivotY : halfHeight) - halfHeight) / halfHeight; // range -1..1
            const F32 halfTargetHeight = targetHeight * 0.5f;
            topLeftY -= halfTargetHeight + halfTargetHeight * normalized;
        }

        vertices[0].Pos.set(topLeftX, topLeftY, 0);
        vertices[1].Pos.set(topLeftX + targetWidth, topLeftY, 0);
        vertices[2].Pos.set(topLeftX, topLeftY + targetHeight, 0);
        vertices[3].Pos.set(topLeftX + targetWidth, topLeftY + targetHeight, 0);
    }

#if T2_IRR_API_VERSION >= 150
    u16 indices[6] = { 0, 1, 2,  3, 2, 1 };
    mDriver->drawVertexPrimitiveList(vertices, 4, indices, 2,
                                     video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
#else // Irrlicht 1.4.x or older
    u16 indices[6] = { 0, 1, 2,  3, 2, 1 };
    mDriver->drawVertexPrimitiveList(vertices, 4, indices, 2,
                                     video::EVT_STANDARD, scene::EPT_TRIANGLES);
#endif
}


} // namespace turska
#endif // T2_USE_IRR
